• Jump To … +
    zoo_frontend/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js zoo_frontend/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.js zoo_frontend/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.min.js zoo_frontend/node_modules/hoist-non-react-statics/src/index.js zoo_frontend/pages/_app.jsx zoo_frontend/pages/_document.jsx zoo_frontend/pages/admin/delivery-containers/index.js zoo_frontend/pages/admin/department/index.js zoo_frontend/pages/admin/group-diets/index.js zoo_frontend/pages/admin/species/index.js zoo_frontend/pages/admin/user/index.js zoo_frontend/pages/diet/edit.js zoo_frontend/pages/diet/index.js zoo_frontend/pages/diet/new.js zoo_frontend/pages/food/dataSrc.js zoo_frontend/pages/food/edit.js zoo_frontend/pages/food/index.js zoo_frontend/pages/food/new.js zoo_frontend/pages/food/nicknames.js zoo_frontend/pages/food/nutrDef.js zoo_frontend/pages/food/units.js zoo_frontend/pages/home/index.js zoo_frontend/pages/index.jsx zoo_frontend/pages/kitchen/index.js zoo_frontend/pages/kitchen/prep/index.js zoo_frontend/pages/login/index.js zoo_frontend/pages/nutritionist/index.js zoo_frontend/pages/print/bin-label.js zoo_frontend/pages/print/index.js zoo_frontend/pages/print/labels.js zoo_frontend/pages/print/prep-sheet.js zoo_frontend/pages/profile/index.js zoo_frontend/pages/reports/cost-by-gl-code.js zoo_frontend/pages/reports/dept-cards.js zoo_frontend/pages/reports/dept-keeper-cards.js zoo_frontend/pages/reports/feeding-cost.js zoo_frontend/pages/reports/index.js zoo_frontend/pages/reports/prep-cards-table.js zoo_frontend/src/api/Animals.js zoo_frontend/src/api/Api.js zoo_frontend/src/api/BudgetIds.js zoo_frontend/src/api/CaseNotes.js zoo_frontend/src/api/DataSrc.js zoo_frontend/src/api/DeliveryContainers.js zoo_frontend/src/api/Departments.js zoo_frontend/src/api/DietChanges.js zoo_frontend/src/api/DietHistory.js zoo_frontend/src/api/DietPlans.js zoo_frontend/src/api/Diets.js zoo_frontend/src/api/Food.js zoo_frontend/src/api/FoodCategories.js zoo_frontend/src/api/FoodPrepTables.js zoo_frontend/src/api/FoodWeights.js zoo_frontend/src/api/LifeStages.js zoo_frontend/src/api/Locations.js zoo_frontend/src/api/NutData.js zoo_frontend/src/api/NutrDef.js zoo_frontend/src/api/PrepNotes.js zoo_frontend/src/api/RoleMappings.js zoo_frontend/src/api/Roles.js zoo_frontend/src/api/Species.js zoo_frontend/src/api/Subenclosures.js zoo_frontend/src/api/Units.js zoo_frontend/src/api/Users.js zoo_frontend/src/api/index.js zoo_frontend/src/components/ConfirmationDialog.jsx zoo_frontend/src/components/ErrorPage.jsx zoo_frontend/src/components/FormCheckbox.jsx zoo_frontend/src/components/Header.jsx zoo_frontend/src/components/KitchenView.jsx zoo_frontend/src/components/Notifications.jsx zoo_frontend/src/components/PrintPrepSheets/PrepSheetPrintOut.jsx zoo_frontend/src/components/PrintPrepSheets/PrintPrepSheets.jsx zoo_frontend/src/components/PrintPrepSheets/index.js zoo_frontend/src/components/ReactSingleSelect.jsx zoo_frontend/src/components/SidebarDrawer.jsx zoo_frontend/src/components/VirtualTable.jsx zoo_frontend/src/components/index.js zoo_frontend/src/getPageContext.js zoo_frontend/src/pages/PageAccess.js zoo_frontend/src/pages/admin/deliveryContainers/deliveryContainers.jsx zoo_frontend/src/pages/admin/deliveryContainers/deliveryContainers.styles.js zoo_frontend/src/pages/admin/deliveryContainers/index.js zoo_frontend/src/pages/admin/department/department.jsx zoo_frontend/src/pages/admin/department/department.styles.js zoo_frontend/src/pages/admin/department/index.js zoo_frontend/src/pages/admin/groupDiets/groupDiets.jsx zoo_frontend/src/pages/admin/groupDiets/groupDiets.styles.js zoo_frontend/src/pages/admin/groupDiets/index.js zoo_frontend/src/pages/admin/species/index.js zoo_frontend/src/pages/admin/species/species.jsx zoo_frontend/src/pages/admin/species/species.styles.js zoo_frontend/src/pages/admin/user/index.js zoo_frontend/src/pages/admin/user/user.jsx zoo_frontend/src/pages/admin/user/user.styles.js zoo_frontend/src/pages/diet/CaseNotesForm.jsx zoo_frontend/src/pages/diet/CurrentDiet.jsx zoo_frontend/src/pages/diet/DietChangeCard.jsx zoo_frontend/src/pages/diet/DietHistory.jsx zoo_frontend/src/pages/diet/DietPlanChangeDialog.jsx zoo_frontend/src/pages/diet/DietSelectDialog.jsx zoo_frontend/src/pages/diet/diet.jsx zoo_frontend/src/pages/diet/diet.styles.js zoo_frontend/src/pages/diet/dietForm.jsx zoo_frontend/src/pages/diet/dietHistoryList.jsx zoo_frontend/src/pages/diet/edit/edit.jsx zoo_frontend/src/pages/diet/edit/edit.styles.js zoo_frontend/src/pages/diet/edit/index.js zoo_frontend/src/pages/diet/index.js zoo_frontend/src/pages/diet/new/index.js zoo_frontend/src/pages/diet/new/new.jsx zoo_frontend/src/pages/diet/new/new.styles.js zoo_frontend/src/pages/diet/prepNotesForm.jsx zoo_frontend/src/pages/food/dataSrc/dataSrc.jsx zoo_frontend/src/pages/food/dataSrc/dataSrc.styles.js zoo_frontend/src/pages/food/dataSrc/index.js zoo_frontend/src/pages/food/edit/FoodWeightTable.jsx zoo_frontend/src/pages/food/edit/edit.jsx zoo_frontend/src/pages/food/edit/edit.styles.js zoo_frontend/src/pages/food/edit/index.js zoo_frontend/src/pages/food/food.jsx zoo_frontend/src/pages/food/food.styles.js zoo_frontend/src/pages/food/foodForm.jsx zoo_frontend/src/pages/food/index.js zoo_frontend/src/pages/food/new/index.js zoo_frontend/src/pages/food/new/new.jsx zoo_frontend/src/pages/food/new/new.styles.js zoo_frontend/src/pages/food/nicknames/index.js zoo_frontend/src/pages/food/nicknames/nicknames.jsx zoo_frontend/src/pages/food/nicknames/nicknames.styles.js zoo_frontend/src/pages/food/nutrDef/index.js zoo_frontend/src/pages/food/nutrDef/nutrDef.jsx zoo_frontend/src/pages/food/nutrDef/nutrDef.styles.js zoo_frontend/src/pages/food/units/index.js zoo_frontend/src/pages/food/units/units.jsx zoo_frontend/src/pages/food/units/units.styles.js zoo_frontend/src/pages/home/home.jsx zoo_frontend/src/pages/home/home.styles.js zoo_frontend/src/pages/home/index.js zoo_frontend/src/pages/kitchen/index.js zoo_frontend/src/pages/kitchen/kitchenHome.jsx zoo_frontend/src/pages/kitchen/kitchenHome.styles.js zoo_frontend/src/pages/kitchen/prep/index.js zoo_frontend/src/pages/kitchen/prep/kitchen.jsx zoo_frontend/src/pages/kitchen/prep/kitchen.styles.js zoo_frontend/src/pages/login/index.js zoo_frontend/src/pages/login/login.jsx zoo_frontend/src/pages/login/login.styles.js zoo_frontend/src/pages/nutritionist/admin.jsx zoo_frontend/src/pages/nutritionist/admin.styles.js zoo_frontend/src/pages/nutritionist/index.js zoo_frontend/src/pages/print/admin.jsx zoo_frontend/src/pages/print/admin.styles.js zoo_frontend/src/pages/print/bin-label/admin.jsx zoo_frontend/src/pages/print/bin-label/admin.styles.js zoo_frontend/src/pages/print/bin-label/index.js zoo_frontend/src/pages/print/index.js zoo_frontend/src/pages/print/labels/admin.jsx zoo_frontend/src/pages/print/labels/admin.styles.js zoo_frontend/src/pages/print/labels/index.js zoo_frontend/src/pages/print/prep-sheet/admin.jsx zoo_frontend/src/pages/print/prep-sheet/admin.styles.js zoo_frontend/src/pages/print/prep-sheet/index.js zoo_frontend/src/pages/profile/index.js zoo_frontend/src/pages/profile/profile.jsx zoo_frontend/src/pages/profile/profile.styles.js zoo_frontend/src/pages/reports/admin.jsx zoo_frontend/src/pages/reports/admin.styles.js zoo_frontend/src/pages/reports/cost-by-gl-code/admin.jsx zoo_frontend/src/pages/reports/cost-by-gl-code/admin.styles.js zoo_frontend/src/pages/reports/cost-by-gl-code/index.js zoo_frontend/src/pages/reports/dept-cards/admin.jsx zoo_frontend/src/pages/reports/dept-cards/admin.styles.js zoo_frontend/src/pages/reports/dept-cards/index.js zoo_frontend/src/pages/reports/dept-keeper-cards/admin.jsx zoo_frontend/src/pages/reports/dept-keeper-cards/admin.styles.js zoo_frontend/src/pages/reports/dept-keeper-cards/index.js zoo_frontend/src/pages/reports/feeding-cost/admin.jsx zoo_frontend/src/pages/reports/feeding-cost/admin.styles.js zoo_frontend/src/pages/reports/feeding-cost/index.js zoo_frontend/src/pages/reports/index.js zoo_frontend/src/pages/reports/prep-cards-table/admin.jsx zoo_frontend/src/pages/reports/prep-cards-table/admin.styles.js zoo_frontend/src/pages/reports/prep-cards-table/index.js zoo_frontend/src/static/LocalStorage.js zoo_frontend/src/static/Roles.js zoo_frontend/src/util/AuthProvider.jsx zoo_frontend/src/util/PageLayout.jsx zoo_frontend/src/util/TableColumnHelper.js zoo_frontend/src/util/WithPropsChecker.jsx zoo_frontend/src/util/camelToNorm.js zoo_frontend/src/util/withAuth.jsx
  • food.jsx

  • ¶
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
  • ¶

    next

    import Link from 'next/link';
    import Router from 'next/router';
  • ¶

    material

    import Typography from '@material-ui/core/Typography';
    import {
      Button, Grid, Paper, Divider, Card,
    } from '@material-ui/core';
  • ¶

    material plugins

    import MaterialTable from 'material-table';
  • ¶

    icons

    import Delete from '@material-ui/icons/Delete';
    import Search from '@material-ui/icons/Search';
    import FirstPage from '@material-ui/icons/FirstPage';
    import LastPage from '@material-ui/icons/LastPage';
    import NextPage from '@material-ui/icons/ChevronRight';
    import PreviousPage from '@material-ui/icons/ChevronLeft';
    import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
    import {
      faInfo, faCheck, faTimes, faEdit,
    } from '@fortawesome/free-solid-svg-icons';
  • ¶

    API helpers

    import FoodAPI from '../../api/Food';
    import FoodCategoryAPI from '../../api/FoodCategories';
    import BudgetCodeAPI from '../../api/BudgetIds';
  • ¶

    access control helpers

    import { hasAccess, Food } from '../PageAccess';
    import Roles from '../../static/Roles';
  • ¶

    util methods

    import camelToNorm from '../../util/camelToNorm';
    
    import { ConfirmationDialog } from '../../components';
    
    import FoodForm from './foodForm';
    
    class FoodPage extends Component {
      /**
       * Server side data retrieval
       */
      static async getInitialProps({ authToken }) {
  • ¶

    api helpers on server side

        const api = new FoodAPI(authToken);
        const categoryAPI = new FoodCategoryAPI(authToken);
        const budgetAPI = new BudgetCodeAPI(authToken);
  • ¶

    server side grab all data for list view

        const res = await api.getFood().catch((err) => ({ foodItems: [{ err: true, msg: err }] }));
  • ¶

    grab all categories because its highly likely that we will use them all for ALL the foods listed

        const foodCategories = await categoryAPI.getCategories().catch(() => { });
        const budgetCodes = await budgetAPI.getBudgetCodes().catch(() => { });
  • ¶

    grab all the budget codes for same reason

        return { foodItems: res.data, foodCategories: foodCategories.data, budgetCodes: budgetCodes.data };
      }
    
      static propTypes = {
        account: PropTypes.object.isRequired,
        classes: PropTypes.object.isRequired,
        foodItems: PropTypes.array.isRequired,
        foodCategories: PropTypes.array.isRequired,
        budgetCodes: PropTypes.array.isRequired,
        token: PropTypes.string.isRequired,
      };
    
      constructor(props) {
        super(props);
        const { budgetCodes, foodCategories, ...rest } = props;
        this.state = {
          ...rest,
          budgetCodes: budgetCodes.map((item) => ({ label: item.budgetCode, value: item.budgetId })),
          foodCategories: foodCategories.map((item) => ({ label: item.foodCategory, value: item.categoryId })),
          newFoodOpen: false,
          newFood: { food: '' }, // changing this will reset the form
          deleteDialogOpen: false,
          dialogRow: {},
        };
    
        this.clientFoodAPI = new FoodAPI(this.props.token);
      }
    
      /**
       * creates detail pane for the food page, this is quick information that helps the nutritionist get information quickly without having to dig
       * @param {Object} rowData data from row in table
       */
      detailHelper(rowData) {
        const relatedRecords = {};
    
        relatedRecords.category = this.props.foodCategories.find((category) => rowData.category === category.categoryId).foodCategory;
        relatedRecords.budgetId = this.props.budgetCodes.find((budget) => rowData.budgetId === budget.budgetId).budgetCode;
    
        const copy = { ...rowData };
  • ¶

    remove keys that are already present

        ['foodId', 'sciName', 'ohdzName', 'food', 'tableData', 'active', 'dry', 'meat', 'preChop', 'preBag'].forEach((key) => delete copy[key]);
  • ¶

    create mini cards with information and link all related information into their respective keys

        const data = Object.keys(copy).map(key => {
          switch (key) {
            case 'category':
            case 'budgetId':
              return (
                <Grid item xs={12} sm={6} lg={3} key={key}>
                  <Card className={this.props.classes.paper}><Typography variant="body1" inline>{camelToNorm(key)}: </Typography><Typography inline variant="body1" color="primary">{String(relatedRecords[key])}</Typography></Card>
                </Grid>
              );
            default:
              return (
                <Grid item xs={12} sm={6} lg={3} key={key}>
                  <Card className={this.props.classes.paper}><Typography variant="body1" inline>{camelToNorm(key)}: </Typography><Typography inline variant="body1" color="primary">{String(copy[key])}</Typography></Card>
                </Grid>
              );
          }
        });
    
        return (
          <div style={{ padding: '10px' }}>
            <Paper style={{ padding: '10px' }}>
              <div style={{ display: 'flex', alignItems: 'center' }}>
                <div>
                  <Typography inline variant="h5">{rowData.food}</Typography>
                  <Typography inline variant="subtitle1" color="textSecondary"> {rowData.ohdzName} </Typography>
                  <div style={{ display: 'flex', justifyContent: 'space-around' }}>
                    <Typography inline variant="subtitle1" color="textSecondary" style={{ marginLeft: '5px', marginRight: '5px' }}>Active: {rowData.active === 1 ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}</Typography>
                    <Typography inline variant="subtitle1" color="textSecondary" style={{ marginLeft: '5px', marginRight: '5px' }}>Dry: {rowData.dry === 1 ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}</Typography>
                    <Typography inline variant="subtitle1" color="textSecondary" style={{ marginLeft: '5px', marginRight: '5px' }}>Meat: {rowData.meat === 1 ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}</Typography>
                    <Typography inline variant="subtitle1" color="textSecondary" style={{ marginLeft: '5px', marginRight: '5px' }}>Pre Chop: {rowData.preChop === 1 ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}</Typography>
                    <Typography inline variant="subtitle1" color="textSecondary" style={{ marginLeft: '5px', marginRight: '5px' }}>Pre Bag: {rowData.preBag === 1 ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}</Typography>
                  </div>
                </div>
                <div style={{ flexGrow: 1 }} />
                <div style={{ display: 'inline-flex', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
                  {hasAccess(this.props.account.role, Food.edit.roles) &&
                    <Link href={`${Food.edit.link}?id=${rowData.foodId}`}>
                      <Button className={this.props.classes.button} color="secondary" variant="contained">
                        <FontAwesomeIcon icon={faEdit} className={this.props.classes.faIcon} />
                        View / Edit
                      </Button>
                    </Link>
                  }
                </div>
              </div>
              <Divider variant="middle" style={{ margin: '10px' }} />
              <Grid container spacing={16}>
                {data}
              </Grid>
            </Paper>
          </div>
        );
      }
    
      createNewFood(payload) {
        if (!payload) {
          return Promise.reject();
        }
        const prom = new Promise((r, rej) => {
          this.clientFoodAPI.createFood(payload).then((res) => {
            this.setState((prevState) => ({ newFoodOpen: false, newFood: { ...res.data }, foodItems: [...prevState.foodItems, res.data] }), () => {
              r();
            });
          }, (rejected) => {
            console.err(rejected.message);
            rej();
          });
        });
        return prom;
      }
    
      async handleDelete(shouldDelete) {
        if (shouldDelete) {
          if (this.state.dialogRow && this.state.dialogRow.foodId) {
            const { foodId } = this.state.dialogRow;
            try {
              await this.clientFoodAPI.deleteFood(foodId);
              this.setState((prevState) => ({ deleteDialogOpen: false, foodItems: prevState.foodItems.filter((item) => item.foodId !== foodId) }));
            } catch (error) {
  • ¶

    clear incorrect state

              this.setState({ deleteDialogOpen: false, dialogRow: {} });
            }
          } else {
  • ¶

    clear incorrect state

            this.setState({ deleteDialogOpen: false, dialogRow: {} });
          }
        }
      }
    
      render() {
        const { role } = this.props.account;
        return (
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
            }}
          >
            {this.state.newFoodOpen &&
              <FoodForm {...this.state.newFood} foodCategories={this.state.foodCategories} budgetCodes={this.state.budgetCodes} submitForm={(payload) => this.createNewFood(payload)} />
            }
            {!this.state.newFoodOpen &&
              <Grid item xs={12} md={3} style={{ padding: '10px' }}>
                <Button onClick={() => { this.setState({ newFoodOpen: true }); }} variant="contained" color="primary">
                  Add New Food
                </Button>
              </Grid>
            }
    
            <div>
              <MaterialTable
                columns={
                  [
                    { title: 'Id', field: 'foodId' },
                    { title: 'Name', field: 'food' },
                    {
                      title: 'Active',
                      field: 'active',
                      render: ((rowData) => (
                        <div className={this.props.classes.activeIcon}>
                          {rowData.active ? <FontAwesomeIcon icon={faCheck} /> : <FontAwesomeIcon icon={faTimes} />}
                        </div>
                      )),
                    },
                  ]
                }
                data={this.state.foodItems}
                options={{
                  pageSize: 20,
                  pageSizeOptions: [20, 50, 100],
                  actionsColumnIndex: -1,
                  emptyRowsWhenPaging: false,
                }}
                icons={{
                  Search,
                  FirstPage,
                  LastPage,
                  NextPage,
                  PreviousPage,
                }}
                title="Food List"
                detailPanel={[
                  {
                    tooltip: 'Food Nutrition Details',
                    icon: () => (<FontAwesomeIcon icon={faInfo} />),
                    render: rowData => (
                      this.detailHelper(rowData)
                    ),
                  },
                ]}
                onRowClick={(event, rowData, togglePanel) => {
                  togglePanel(0);
                }}
                actions={[
                  {
                    icon: () => (<FontAwesomeIcon icon={faEdit} />),
                    tooltip: 'View / Edit',
                    disabled: !hasAccess(role, Food.edit.roles),
                    onClick: (event, rowData) => {
                      Router.push({
                        pathname: Food.edit.link,
                        query: { id: rowData.foodId },
                      });
                    },
                  },
                  {
                    disabled: !hasAccess(this.props.account.role, [Roles.ADMIN]),
                    icon: () => <Delete />,
                    onClick: (evt, row) => {
                      this.setState({ deleteDialogOpen: true, dialogRow: row });
                    },
                    tooltip: 'Delete Food',
                  },
                ]}
              />
            </div>
            <ConfirmationDialog
              open={this.state.deleteDialogOpen}
              onClose={(close) => this.handleDelete(close)}
              title="Are you sure you want to delete this nutrient record?"
            />
          </div>
        );
      }
    }
    
    export default FoodPage;